package ms.maplestory.boot.loaders;

import ms.maplestory.boot.ServerConstants;
import ms.maplestory.boot.loaders.container.SkillStringInfo;
import lombok.extern.slf4j.Slf4j;
import ms.maplestory.boot.util.Util;
import ms.maplestory.boot.util.XMLApi;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j(topic = "[Loaders String]")
public class StringData {

    public static Map<Integer, String> itemStrings = new HashMap<>();
    public static Map<Integer, SkillStringInfo> skillString = new HashMap<>();
    public static Map<Integer, String> mobStrings = new HashMap<>();
    public static Map<Integer, String> npcStrings = new HashMap<>();
    public static Map<Integer, String> mapStrings = new HashMap<>();

    public static void generateDatFiles() {
        log.info("Started generating string data.");
        long start = System.currentTimeMillis();
        loadItemStringsFromWz();
        loadSkillStringsFromWz();
        loadMobStringsFromWz();
        loadNpcStringsFromWz();
        loadMapStringsFromWz();
        saveSkillStringsToDat(ServerConstants.DAT_DIR + "/strings");
        saveItemStringsToDat(ServerConstants.DAT_DIR + "/strings");
        saveMobStringsToDat(ServerConstants.DAT_DIR + "/strings");
        saveNpcStringsToDat(ServerConstants.DAT_DIR + "/strings");
        saveMapStringsToDat(ServerConstants.DAT_DIR + "/strings");
        log.info("Completed generating string data in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadItemStringsFromWz() {
        log.info("Started loading item strings from wz.");
        long start = System.currentTimeMillis();
        String wzDir = ServerConstants.WZ_DIR + "/String.wz/";
        String[] files = new String[]{"Cash", "Consume", "Eqp", "Ins", "Pet", "Etc"};
        for (String fileDir : files) {
            File file = new File(wzDir + fileDir + ".img.xml");
            Document doc = XMLApi.getRoot(file);
            Node node = doc;
            List<Node> nodes = XMLApi.getAllChildren(node);
            for (Node topNode : nodes) {
                if (!fileDir.equalsIgnoreCase("eqp") &&
                        !fileDir.equalsIgnoreCase("etc")) {
                    for (Node mainNode : XMLApi.getAllChildren(topNode)) {
                        int id = Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name"));
                        String name = XMLApi.getNamedAttribute(XMLApi.getFirstChildByNameBF(mainNode, "name"), "value");
                        itemStrings.put(id, name);
                    }
                } else if (fileDir.equalsIgnoreCase("etc")) {
                    for (Node category : XMLApi.getAllChildren(topNode)) {
                        for (Node mainNode : XMLApi.getAllChildren(category)) {
                            int id = Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name"));
                            if (XMLApi.getFirstChildByNameBF(mainNode, "name") != null) {
                                String name = XMLApi.getNamedAttribute(XMLApi.getFirstChildByNameBF(mainNode, "name"), "value");
                                itemStrings.put(id, name);
                            }
                        }
                    }
                } else {
                    for (Node n : XMLApi.getAllChildren(topNode)) {
                        for (Node category : XMLApi.getAllChildren(n)) {
                            for (Node mainNode : XMLApi.getAllChildren(category)) {
                                int id = Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name"));
                                String name = "";
                                if (XMLApi.getFirstChildByNameBF(mainNode, "name") != null) {
                                    name = XMLApi.getNamedAttribute(XMLApi.getFirstChildByNameBF(mainNode, "name"), "value");
                                }
                                itemStrings.put(id, name);
                            }
                        }
                    }
                }
            }
        }
        log.info("Loaded item strings from wz in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadSkillStringsFromWz() {
        log.info("Started loading skill strings from wz.");
        long start = System.currentTimeMillis();
        String wzDir = ServerConstants.WZ_DIR + "/String.wz/Skill.img.xml";
        File file = new File(wzDir);
        Document doc = XMLApi.getRoot(file);
        Node node = doc;
        List<Node> nodes = XMLApi.getAllChildren(node);
        for (Node topNode : nodes) {
            for (Node mainNode : XMLApi.getAllChildren(topNode)) {
                Node bookNameNode = XMLApi.getFirstChildByNameBF(mainNode, "bookName");
                if (bookNameNode != null) {
                    continue;
                }
                SkillStringInfo ssi = new SkillStringInfo();
                Node nameNode = XMLApi.getFirstChildByNameBF(mainNode, "name");
                if (nameNode != null) {
                    ssi.setName(XMLApi.getNamedAttribute(nameNode, "value"));
                }
                Node descNode = XMLApi.getFirstChildByNameBF(mainNode, "desc");
                if (descNode != null) {
                    ssi.setDesc(XMLApi.getNamedAttribute(descNode, "value"));
                }
                Node hNode = XMLApi.getFirstChildByNameBF(mainNode, "h");
                if (hNode != null) {
                    ssi.setH(XMLApi.getNamedAttribute(hNode, "value"));
                } else {
                    Node h1Node = XMLApi.getFirstChildByNameBF(mainNode, "h1");
                    if (h1Node != null) {
                        ssi.setH(XMLApi.getNamedAttribute(h1Node, "value"));
                    }
                }
                skillString.put(Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name")), ssi);
            }
        }
        log.info("Loaded skill strings in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadMobStringsFromWz() {
        log.info("Started loading mob strings from wz.");
        long start = System.currentTimeMillis();
        String wzDir = ServerConstants.WZ_DIR + "/String.wz/Mob.img.xml";
        File file = new File(wzDir);
        Document doc = XMLApi.getRoot(file);
        List<Node> nodes = XMLApi.getAllChildren(doc);
        for (Node topNode : nodes) {
            for (Node mainNode : XMLApi.getAllChildren(topNode)) {
                int id = Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name"));
                for (Node infoNode : XMLApi.getAllChildren(mainNode)) {
                    String name = XMLApi.getNamedAttribute(infoNode, "name");
                    String value = XMLApi.getNamedAttribute(infoNode, "value");
                    switch (name) {
                        case "name":
                            mobStrings.put(id, value);
                            break;
                    }
                }
            }
        }
        log.info("Loaded mob strings in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadNpcStringsFromWz() {
        log.info("Started loading npc strings from wz.");
        long start = System.currentTimeMillis();
        String wzDir = ServerConstants.WZ_DIR + "/String.wz/Npc.img.xml";
        File file = new File(wzDir);
        Document doc = XMLApi.getRoot(file);
        List<Node> nodes = XMLApi.getAllChildren(doc);
        for (Node topNode : nodes) {
            for (Node mainNode : XMLApi.getAllChildren(topNode)) {
                int id = Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name"));
                for (Node infoNode : XMLApi.getAllChildren(mainNode)) {
                    String name = XMLApi.getNamedAttribute(infoNode, "name");
                    String value = XMLApi.getNamedAttribute(infoNode, "value");
                    switch (name) {
                        case "name":
                            npcStrings.put(id, value);
                            break;
                    }
                }
            }
        }
        log.info("Loaded npc strings in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadMapStringsFromWz() {
        log.info("Started loading map strings from wz.");
        long start = System.currentTimeMillis();
        String wzDir = ServerConstants.WZ_DIR + "/String.wz/Map.img.xml";
        File file = new File(wzDir);
        Document doc = XMLApi.getRoot(file);
        List<Node> nodes = XMLApi.getAllChildren(doc);
        for (Node topNode : nodes) {
            for (Node areaNode : XMLApi.getAllChildren(topNode)) {
                for (Node mainNode : XMLApi.getAllChildren(areaNode)) {
                    int id = Integer.parseInt(XMLApi.getNamedAttribute(mainNode, "name"));
                    String mapName = "UNK";
                    String streetName = "UNK";
                    for (Node infoNode : XMLApi.getAllChildren(mainNode)) {
                        String name = XMLApi.getNamedAttribute(infoNode, "name");
                        String value = XMLApi.getNamedAttribute(infoNode, "value");
                        switch (name) {
                            case "mapName":
                                mapName = value;
                                break;
                            case "streetName":
                                streetName = value;
                                break;
                        }
                    }
                    mapStrings.put(id, String.format("%s : %s", streetName, mapName));
                }
            }
        }
        log.info("Loaded map strings in {}ms.", System.currentTimeMillis() - start);
    }

    private static void saveItemStringsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        File file = new File(dir + "/items.dat");
        try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file))) {
            dataOutputStream.writeInt(itemStrings.size());
            for (Map.Entry<Integer, String> entry : itemStrings.entrySet()) {
                int id = entry.getKey();
                String ssi = entry.getValue();
                dataOutputStream.writeInt(id);
                dataOutputStream.writeUTF(ssi);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void saveSkillStringsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        File file = new File(dir + "/skills.dat");
        try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file))) {
            dataOutputStream.writeInt(skillString.size());
            for (Map.Entry<Integer, SkillStringInfo> entry : skillString.entrySet()) {
                int id = entry.getKey();
                SkillStringInfo ssi = entry.getValue();
                dataOutputStream.writeInt(id);
                dataOutputStream.writeUTF(ssi.getName() == null ? "" : ssi.getName());
                dataOutputStream.writeUTF(ssi.getDesc());
                dataOutputStream.writeUTF(ssi.getH());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void saveMobStringsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        File file = new File(dir + "/mobs.dat");
        try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file))) {
            dataOutputStream.writeInt(mobStrings.size());
            for (Map.Entry<Integer, String> entry : mobStrings.entrySet()) {
                int id = entry.getKey();
                String name = entry.getValue();
                dataOutputStream.writeInt(id);
                dataOutputStream.writeUTF(name);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void saveNpcStringsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        File file = new File(dir + "/npcs.dat");
        try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file))) {
            dataOutputStream.writeInt(npcStrings.size());
            for (Map.Entry<Integer, String> entry : npcStrings.entrySet()) {
                int id = entry.getKey();
                String name = entry.getValue();
                dataOutputStream.writeInt(id);
                dataOutputStream.writeUTF(name);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void saveMapStringsToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        File file = new File(dir + "/maps.dat");
        try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(file))) {
            dataOutputStream.writeInt(mapStrings.size());
            for (Map.Entry<Integer, String> entry : mapStrings.entrySet()) {
                int id = entry.getKey();
                String name = entry.getValue();
                dataOutputStream.writeInt(id);
                dataOutputStream.writeUTF(name);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void loadStringFromDat() {
        loadItemStringsFromDat();
        loadSkillStringsFromDat();
        loadMobStringsFromDat();
        loadNpcStringsFromDat();
        loadMapStringsFromDat();
    }

    public static void loadItemStringsFromDat() {
        long start = System.currentTimeMillis();
        File file = new File(ServerConstants.DAT_DIR + "/strings/items.dat");
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file))) {
            int size = dataInputStream.readInt();
            for (int i = 0; i < size; i++) {
                int id = dataInputStream.readInt();
                String name = dataInputStream.readUTF();
                itemStrings.put(id, name);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("Loaded item strings from data file in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadSkillStringsFromDat() {
        long start = System.currentTimeMillis();
        File file = new File(ServerConstants.DAT_DIR + "/strings/skills.dat");
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file))) {
            int size = dataInputStream.readInt();
            for (int i = 0; i < size; i++) {
                int id = dataInputStream.readInt();
                SkillStringInfo ssi = new SkillStringInfo();
                ssi.setName(dataInputStream.readUTF());
                ssi.setDesc(dataInputStream.readUTF());
                ssi.setH(dataInputStream.readUTF());
                skillString.put(id, ssi);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("Loaded skill strings from data file in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadMobStringsFromDat() {
        long start = System.currentTimeMillis();
        File file = new File(ServerConstants.DAT_DIR + "/strings/mobs.dat");
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file))) {
            int size = dataInputStream.readInt();
            for (int i = 0; i < size; i++) {
                int id = dataInputStream.readInt();
                String name = dataInputStream.readUTF();
                mobStrings.put(id, name);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("Loaded mob strings from data file in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadNpcStringsFromDat() {
        long start = System.currentTimeMillis();
        File file = new File(ServerConstants.DAT_DIR + "/strings/npcs.dat");
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file))) {
            int size = dataInputStream.readInt();
            for (int i = 0; i < size; i++) {
                int id = dataInputStream.readInt();
                String name = dataInputStream.readUTF();
                npcStrings.put(id, name);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("Loaded npc strings from data file in {}ms.", System.currentTimeMillis() - start);
    }

    public static void loadMapStringsFromDat() {
        long start = System.currentTimeMillis();
        File file = new File(ServerConstants.DAT_DIR + "/strings/maps.dat");
        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(file))) {
            int size = dataInputStream.readInt();
            for (int i = 0; i < size; i++) {
                int id = dataInputStream.readInt();
                String name = dataInputStream.readUTF();
                mapStrings.put(id, name);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("Loaded item strings from data file in {}ms.", System.currentTimeMillis() - start);
    }

}
